/* ------------------------------------------------------------ */
/*
HTTrack Website Copier, Offline Browser for Windows and Unix
Copyright (C) 1998-2017 Xavier Roche and other contributors

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Please visit our Website: http://www.httrack.com
*/

/* ------------------------------------------------------------ */
/* File: ProxyTrack, httrack cache-based proxy                  */
/* Author: Xavier Roche                                         */
/* ------------------------------------------------------------ */

#ifndef WEBHTTRACK_PROXYTRACK
#define WEBHTTRACK_PROXYTRACK

/* Version */
#define PROXYTRACK_VERSION "0.5"

/* Store manager */
#include "../minizip/mztools.h"
#include "store.h"

#include <sys/stat.h>
#ifndef HTS_DO_NOT_USE_FTIME
#ifdef _WIN32
#include <sys/utime.h>
#else
#include <utime.h>
#endif
#include <sys/timeb.h>
#else
#include <utime.h>
#endif
#ifndef _WIN32
#include <pthread.h>
#endif
#include <stdarg.h>

/* generic */

int proxytrack_main(char *proxyAddr, int proxyPort, char *icpAddr, int icpPort,
                    PT_Indexes index);

/* Spaces: CR,LF,TAB,FF */
#define  is_space(c)      ( ((c)==' ') || ((c)=='\"') || ((c)==10) || ((c)==13) || ((c)==9) || ((c)==12) || ((c)==11) || ((c)=='\'') )
#define  is_realspace(c)  ( ((c)==' ')                || ((c)==10) || ((c)==13) || ((c)==9) || ((c)==12) || ((c)==11)                )
#define  is_taborspace(c) ( ((c)==' ')                                          || ((c)==9)                             )
#define  is_quote(c)      (               ((c)=='\"')                                                    || ((c)=='\'') )
#define  is_retorsep(c)   (                              ((c)==10) || ((c)==13) || ((c)==9)                                          )

/* Static definitions */

HTS_UNUSED static void proxytrack_print_log(const char *severity, const char *format, ...) {
  if (severity != NULL) {
    const int error = errno;
    FILE *const fp = stderr;
    va_list args;

    fprintf(fp, " * %s: ", severity);
    va_start(args, format);
    (void) vfprintf(fp, format, args);
    va_end(args);
    fputs("\n", fp);
    fflush(fp);
    errno = error;
  }
}

#define CRITICAL "critical"
#define WARNING "warning"
#define LOG "log"
#if defined(_DEBUG) || defined(DEBUG)
#define DEBUG "debug"
#else
#define DEBUG NULL
#endif

/* Header for generated pages */
#define PROXYTRACK_COMMENT_HEADER \
	"<!-- Generated by ProxyTrack " PROXYTRACK_VERSION " -->\r\n" \
	"<!-- This is an add-on for HTTrack " HTTRACK_VERSIONID " -->\r\n"

/* See IE "feature" (MSKB Q294807) */
#define DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES											\
	"<!-- Start Disable IE Friendly HTTP Error Messages -->\r\n"			\
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- _-._.--._._-._.--._._-._.--._._-._.--._._-._.--._. -->\r\n" \
	"<!-- End Disable IE Friendly HTTP Error Messages -->\r\n"

HTS_UNUSED static const char *gethomedir(void) {
  const char *home = getenv("HOME");

  if (home)
    return home;
  else
    return ".";
}

HTS_UNUSED static int linput(FILE * fp, char *s, int max) {
  int c;
  int j = 0;

  do {
    c = fgetc(fp);
    if (c != EOF) {
      switch (c) {
      case 13:
        break;                  // sauter CR
      case 10:
        c = -1;
        break;
      case 0:
      case 9:
      case 12:
        break;                  // sauter ces caractères
      default:
        s[j++] = (char) c;
        break;
      }
    }
  } while((c != -1) && (c != EOF) && (j < (max - 1)));
  s[j] = '\0';
  return j;
}

HTS_UNUSED static int link_has_authority(const char *lien) {
  const char *a = lien;

  if (isalpha((const unsigned char) *a)) {
    // Skip scheme?
    while(isalpha((const unsigned char) *a))
      a++;
    if (*a == ':')
      a++;
    else
      return 0;
  }
  if (strncmp(a, "//", 2) == 0)
    return 1;
  return 0;
}

HTS_UNUSED static const char *jump_protocol(const char *source) {
  int p;

  // scheme
  // "Comparisons of scheme names MUST be case-insensitive" (RFC2616)
  if ((p = strfield(source, "http:")))
    source += p;
  else if ((p = strfield(source, "ftp:")))
    source += p;
  else if ((p = strfield(source, "https:")))
    source += p;
  else if ((p = strfield(source, "file:")))
    source += p;
  // net_path
  if (strncmp(source, "//", 2) == 0)
    source += 2;
  return source;
}

HTS_UNUSED static const char *strrchr_limit(const char *s, char c, const char *limit) {
  if (limit == NULL) {
    char *p = strrchr(s, c);

    return p ? (p + 1) : NULL;
  } else {
    char *a = NULL, *p;

    for(;;) {
      p = strchr((a) ? a : s, c);
      if ((p >= limit) || (p == NULL))
        return a;
      a = p + 1;
    }
  }
}

HTS_UNUSED static const char *jump_protocol_and_auth(const char *source) {
  const char *a, *trytofind;

  if (strcmp(source, "file://") == 0)
    return source;
  a = jump_protocol(source);
  trytofind = strrchr_limit(a, '@', strchr(a, '/'));
  return (trytofind != NULL) ? trytofind : a;
}

#ifndef min
#define min(a,b) ((a)>(b)?(b):(a))
#endif
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
HTS_UNUSED static int linput_trim(FILE * fp, char *s, int max) {
  int rlen = 0;
  char *const ls = (char *) malloc(max + 1);

  s[0] = '\0';
  if (ls) {
    char *a;

    // lire ligne
    rlen = linput(fp, ls, max);
    if (rlen) {
      // sauter espaces et tabs en fin
      while((rlen > 0) && is_realspace(ls[max(rlen - 1, 0)]))
        ls[--rlen] = '\0';
      // sauter espaces en début
      a = ls;
      while((rlen > 0) && ((*a == ' ') || (*a == '\t'))) {
        a++;
        rlen--;
      }
      if (rlen > 0) {
        memcpy(s, a, rlen);     // can copy \0 chars
        s[rlen] = '\0';
      }
    }
    //
    free(ls);
  }
  return rlen;
}

#ifndef S_ISREG
#define S_ISREG(m) ((m) & _S_IFREG)
#endif
HTS_UNUSED static int fexist(char *s) {
  struct stat st;

  memset(&st, 0, sizeof(st));
  if (stat(s, &st) == 0) {
    if (S_ISREG(st.st_mode)) {
      return 1;
    }
  }
  return 0;
}

/* convertir une chaine en temps */
HTS_UNUSED static void set_lowcase(char *s) {
  int i;

  for(i = 0; i < (int) strlen(s); i++)
    if ((s[i] >= 'A') && (s[i] <= 'Z'))
      s[i] += ('a' - 'A');
}
HTS_UNUSED static struct tm *convert_time_rfc822(struct tm *result, const char *s) {
  char months[] = "jan feb mar apr may jun jul aug sep oct nov dec";
  char str[256];
  char *a;

  /* */
  int result_mm = -1;
  int result_dd = -1;
  int result_n1 = -1;
  int result_n2 = -1;
  int result_n3 = -1;
  int result_n4 = -1;

  /* */

  if ((int) strlen(s) > 200)
    return NULL;
  strcpy(str, s);
  set_lowcase(str);
  /* éliminer :,- */
  while((a = strchr(str, '-')))
    *a = ' ';
  while((a = strchr(str, ':')))
    *a = ' ';
  while((a = strchr(str, ',')))
    *a = ' ';
  /* tokeniser */
  a = str;
  while(*a) {
    char *first, *last;
    char tok[256];

    /* découper mot */
    while(*a == ' ')
      a++;                      /* sauter espaces */
    first = a;
    while((*a) && (*a != ' '))
      a++;
    last = a;
    tok[0] = '\0';
    if (first != last) {
      char *pos;

      strncat(tok, first, (int) (last - first));
      /* analyser */
      if ((pos = strstr(months, tok))) {        /* month always in letters */
        result_mm = ((int) (pos - months)) / 4;
      } else {
        int number;

        if (sscanf(tok, "%d", &number) == 1) {  /* number token */
          if (result_dd < 0)    /* day always first number */
            result_dd = number;
          else if (result_n1 < 0)
            result_n1 = number;
          else if (result_n2 < 0)
            result_n2 = number;
          else if (result_n3 < 0)
            result_n3 = number;
          else if (result_n4 < 0)
            result_n4 = number;
        }                       /* sinon, bruit de fond(+1GMT for exampel) */
      }
    }
  }
  if ((result_n1 >= 0) && (result_mm >= 0) && (result_dd >= 0)
      && (result_n2 >= 0) && (result_n3 >= 0) && (result_n4 >= 0)) {
    if (result_n4 >= 1000) {    /* Sun Nov  6 08:49:37 1994 */
      result->tm_year = result_n4 - 1900;
      result->tm_hour = result_n1;
      result->tm_min = result_n2;
      result->tm_sec = max(result_n3, 0);
    } else {                    /* Sun, 06 Nov 1994 08:49:37 GMT or Sunday, 06-Nov-94 08:49:37 GMT */
      result->tm_hour = result_n2;
      result->tm_min = result_n3;
      result->tm_sec = max(result_n4, 0);
      if (result_n1 <= 50)      /* 00 means 2000 */
        result->tm_year = result_n1 + 100;
      else if (result_n1 < 1000)        /* 99 means 1999 */
        result->tm_year = result_n1;
      else                      /* 2000 */
        result->tm_year = result_n1 - 1900;
    }
    result->tm_isdst = 0;       /* assume GMT */
    result->tm_yday = -1;       /* don't know */
    result->tm_wday = -1;       /* don't know */
    result->tm_mon = result_mm;
    result->tm_mday = result_dd;
    return result;
  }
  return NULL;
}
HTS_UNUSED static struct tm PT_GetTime(time_t t) {
  struct tm tmbuf;

#ifdef _WIN32
  struct tm *tm = gmtime(&t);
#else
  struct tm *tm = gmtime_r(&t, &tmbuf);
#endif
  if (tm != NULL)
    return *tm;
  else {
    memset(&tmbuf, 0, sizeof(tmbuf));
    return tmbuf;
  }
}
HTS_UNUSED static int set_filetime(const char *file, struct tm *tm_time) {
  struct utimbuf tim;

#ifndef HTS_DO_NOT_USE_FTIME
  struct timeb B;

  memset(&B, 0, sizeof(B));
  B.timezone = 0;
  ftime(&B);
  tim.actime = tim.modtime = mktime(tm_time) - B.timezone * 60;
#else
  // bogus time (GMT/local)..
  tim.actime = tim.modtime = mktime(tm_time);
#endif
  return utime(file, &tim);
}
HTS_UNUSED static int set_filetime_time_t(const char *file, time_t t) {
  if (t != (time_t) 0 && t != (time_t) - 1) {
    struct tm tm = PT_GetTime(t);

    return set_filetime(file, &tm);
  }
  return -1;
}
HTS_UNUSED static int set_filetime_rfc822(const char *file, const char *date) {
  struct tm buffer;
  struct tm *tm_s = convert_time_rfc822(&buffer, date);

  if (tm_s) {
    return set_filetime(file, tm_s);
  } else
    return -1;
}

#endif
